#ifndef XAPPLICATION_H
#define XAPPLICATION_H
#include <iostream>
#include <string>
#include <functional>
#include <map>
#include <regex>
#include <globalVar.h>

using predicateFun_t =  std::function<bool(std::string)>;
using CallBack_t =std::function<void()>;

template<typename identifier>
using getCallBack_ID_t = std::function<identifier(std::string)>; 

template<typename identifier>
class XApplication
{
public:
    XApplication(int argc,char* argv[]){
        globalFunction::setCurrentExeDir(argv[0]);
    }
   
    int exec(getCallBack_ID_t<identifier> getCB_ID,predicateFun_t fn = std::bind(&XApplication::isDigtal,std::placeholders::_1))
    {
        while (true)
        { 
           std::string input;
           std::cout<<"Please enter a message"<<std::endl; 
           std::getline(std::cin, input);
            if (!std::cin){
                std::cout<<"XApplication recive exit signal"<<std::endl;
                std::cin.clear();
                std::cin.sync();
                return 0;
            }
            if (input == "list")
            {
                if (m_content.size() > 0)
                {
                    std::cout << m_content;
                    std::cin.clear();
                    std::cin.sync();
                }
                continue;
            }
            if (fn(input))
            {
                identifier id = getCB_ID(input);
                CallBack_t cb = getCallBack(id);
                if(cb)
                    cb();
                else
                    std::cout<<"No behavior defined for this data"<<std::endl;
            }
            else
            {
                std::cout << input << "Not a valid data type" << std::endl;
                std::cin.clear();
                std::cin.sync();
            }
        }
    }

    template<typename F,typename ...Args>
    bool registerCallback(identifier id,F&&f,Args&&... args){
        if(s_callbacks.find(id) != s_callbacks.end())
            return false;

        CallBack_t obj = std::bind(std::forward<F>(f),std::forward<Args>(args)...);
        s_callbacks.insert(std::pair<identifier,CallBack_t>(id,std::move(obj)));
        return true;
    }

    CallBack_t getCallBack(identifier id){
        if(s_callbacks.find(id) != s_callbacks.end())
            return s_callbacks.at(id);
        return CallBack_t();
    }

    static bool isDigtal(std::string str)
    {
        regex reg1("[0-9]+");
        return regex_match(str, reg1);
    }
public:
    std::string m_content;
    static  std::map<identifier,CallBack_t> s_callbacks;
};

template<typename identifier>
std::map<identifier,CallBack_t> XApplication<identifier>::s_callbacks;
#endif